//FACEBOOK ASYNC CLIENT CLASS IMPLEMENTATION

#include "facebookasync.h"

#include "xmlParser.h"
#include "md5.h"
#include <fstream>
#include <iostream>

//CURRENT USER ROUTINES

bool FacebookAsync::CurUser_StartLogin()
{

	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartLogin(). Current user was not set!"; return false;
	}

	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartLogin(). FacebookAsync::Init() wasn't called!"; return false;
	}

	if (m_pCurUser->m_CurLoginStage == LOGINSTAGE_SUCCEED)
		return true;

	if (m_pCurUser->m_CurLoginStage != LOGINSTAGE_NOTSTARTED && m_pCurUser->m_CurLoginStage != LOGINSTAGE_FAILED)
	{
		return true;	
	}

	m_ErrorDescription = "";
	m_FacebookSiteChanged = false;
	m_InvalidLoginPassword = false;


	return m_pCurUser->StartLogin();

}


FACEBOOK_LOGINSTAGES FacebookAsync::CurUser_GetLoginStage()
{
	if (m_pCurUser == NULL)
		return LOGINSTAGE_NOTSTARTED;

	return m_pCurUser->m_CurLoginStage;
}


FACEBOOK_INFOSTAGES FacebookAsync::CurUser_GetInfoStage()
{
	if (m_pCurUser == NULL)
		return INFOSTAGE_NOTSTARTED;

	return m_pCurUser->m_CurInfoStage;
}


FACEBOOK_PHOTOSTAGES FacebookAsync::CurUser_GetPhotoStage()
{
	if (m_pCurUser == NULL)
		return PHOTOSTAGE_NOTSTARTED;

	return m_pCurUser->m_CurPhotoStage;

}


FACEBOOK_NOTIFICATIONSTAGES FacebookAsync::CurUser_GetNotificationStage()
{
	if (m_pCurUser == NULL)
		return NOTIFSTAGE_NOTSTARTED;

	return m_pCurUser->m_CurNotificationStage;

}


FACEBOOK_POSTFEEDITEMSTAGES FacebookAsync::CurUser_GetPostFeedItemStage()
{
	if (m_pCurUser == NULL)
		return POSTFEEDSTAGE_NOTSTARTED;

	return m_pCurUser->m_CurPostFeedItemStage; 

}




bool FacebookAsync::CurUser_StartFetchingInfo()
{

	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingInfo(). Current user was not set!"; return false;
	}

	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingInfo(). FacebookAsync::Init() wasn't called!"; return false;
	}

	if (m_pCurUser->m_CurLoginStage != LOGINSTAGE_SUCCEED)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingInfo(). User was not log in!"; return false;
	}

	if (m_pCurUser->m_CurNotificationStage == NOTIFSTAGE_INPROGRESS)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingPhotos(). Notification in progress!"; return false;
	}

	if (m_pCurUser->m_CurPostFeedItemStage == POSTFEEDSTAGE_INPROGRESS)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). posting to feed in progress!"; return false;
	}

	if (m_pCurUser->m_CurInfoStage != INFOSTAGE_NOTSTARTED && m_pCurUser->m_CurInfoStage != INFOSTAGE_FAILED)
	{
		return true;	
	}

	if (m_pCurUser->m_GenericCallInProgress == true)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingPhotos(). Generic call in progress!"; return false;
	}

	m_ErrorDescription = "";
	m_FacebookSiteChanged = false;
	m_InvalidLoginPassword = false;


	return m_pCurUser->StartFetchingInfo();
}


bool FacebookAsync::CurUser_StartFetchingPhotos()
{

	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingPhotos(). Current user was not set!"; return false;
	}

	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingPhotos(). FacebookAsync::Init() wasn't called!"; return false;
	}

	if (m_pCurUser->m_CurLoginStage != LOGINSTAGE_SUCCEED)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingPhotos(). User was not log in!"; return false;
	}

	if (m_pCurUser->m_CurInfoStage != INFOSTAGE_SUCCEED)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingPhotos(). No user info fetched!"; return false;
	}

	if (m_pCurUser->m_CurNotificationStage == NOTIFSTAGE_INPROGRESS)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingPhotos(). Notification in progress!"; return false;
	}

	if (m_pCurUser->m_CurPostFeedItemStage == POSTFEEDSTAGE_INPROGRESS)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingPhotos(). posting to feed in progress!"; return false;
	}

	if (m_pCurUser->m_GenericCallInProgress == true)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_StartFetchingPhotos(). Generic call in progress!"; return false;
	}


	if (m_pCurUser->m_CurPhotoStage != PHOTOSTAGE_NOTSTARTED && m_pCurUser->m_CurPhotoStage != PHOTOSTAGE_FAILED)
	{
		return true;	
	}

	m_ErrorDescription = "";
	m_FacebookSiteChanged = false;
	m_InvalidLoginPassword = false;


	return m_pCurUser->StartFetchingPhotos();

}



bool FacebookAsync::CurUser_SetAutoFetch(bool AutoFetch)
{
	if (m_pCurUser == NULL)
		return false;

	m_pCurUser->m_AutoFetch = AutoFetch;
	return true;
}



bool FacebookAsync::AddApplicationToProfile(const std::string& AppToAddKey, bool AllowProfile, bool AllowNavLink, bool AllowFeeds, bool AllowProfileActions)
{

	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "FacebookAsync::AddApplicationToProfile(). Current user was not set!"; return false;
	}

	if (m_pCurUser->m_CurLoginStage != LOGINSTAGE_SUCCEED)
	{
		m_ErrorDescription = "FacebookAsync::AddApplicationToProfile(). Current user was not logged in!"; return false;
	}

	if (m_pCurUser->m_CurInfoStage != INFOSTAGE_SUCCEED)
	{
		m_ErrorDescription = "FacebookAsync::AddApplicationToProfile(). Current user profile info must be fetched before calling this method!"; return false;
	}

	if (m_pCurUser->m_CurPhotoStage != PHOTOSTAGE_NOTSTARTED && m_pCurUser->m_CurPhotoStage != PHOTOSTAGE_SUCCEED && m_pCurUser->m_CurPhotoStage != PHOTOSTAGE_FAILED)
	{
		m_ErrorDescription = "FacebookAsync::AddApplicationToProfile(). fetching photos may be in progress!"; return false;
	}
	
	if (m_pCurUser->m_GenericCallInProgress == true)
	{
		m_ErrorDescription = "FacebookAsync::AddApplicationToProfile(). Generic call in progress!"; return false;
	}

	//STEP 1. Making initial GET request. Black magic begins!

	//resetting the curl
	curl_easy_reset(m_pCurUser->m_pCurl);

	//creating url for get request
	std::string GetRequestURL = FACEBOOK_ADDAPPURL;

	//appending params
	GetRequestURL.append("?api_key=");
	GetRequestURL.append(AppToAddKey);
	GetRequestURL.append("&ref=pd");   //used as in browser


	//providing url to curl
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_URL, GetRequestURL.c_str());

	//telling curl it will be a GET request
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_HTTPGET, true);

	//enabling cookies
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_COOKIEFILE, "");

	//enabling redirections
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_MAXREDIRS, 100);

	//setting user agent - a subject to think about
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_USERAGENT, FACEBOOK_USERAGENT);

	//setting the request result writing
	std::string RequestResult = "";

	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEFUNCTION, Facebook::Write_Callback);
    curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEDATA, &RequestResult);	

	//making the request
	CURLcode RequestResultCode;
	RequestResultCode = curl_easy_perform(m_pCurUser->m_pCurl);

	//checking if request was ok
	if (RequestResultCode != 0)
	{
		m_ErrorDescription = "FacebookAsync::AddApplicationToProfile(). Step 1 error. Can't make HTTP request: initial GET request!"; return false;	
	}

	if (RequestResult.size() < 1)
	{
		m_ErrorDescription = "FacebookAsync::AddApplicationToProfile(). Step 1 error. Request result is empty(initial GET request)!"; return false;	
	}

	//std::ofstream tmpFile1("r1.txt");
	//tmpFile1 << RequestResult;
	//tmpFile1.close();

	//checking if app was already added
	std::string::size_type FindLoc;

	FindLoc = RequestResult.find("confirm_ive_got_nothing");

	if (FindLoc != std::string::npos)
	{
		//app was already added
		return true;
	}


	//THESE CHECKS ARE DISABLED!
	//making some tests to be sure that the facebook structure wasn't changed
	//FindLoc = RequestResult.find("to your Facebook account?");

	//if (FindLoc == std::string::npos)
	//{
	//	m_ErrorDescription = "Facebook::AddApplicationToProfile(). Step 3 error. Request result is a wrong page! Something was changed on facebook!"; return false;	
	//	m_FacebookSiteChanged = true;
	//	return false;
	//}

	//FindLoc = RequestResult.find("Allow this application to");

	//if (FindLoc == std::string::npos)
	//{
	//	m_ErrorDescription = "Facebook::AddApplicationToProfile(). Step 3 error. Request result is a wrong page! Something was changed on facebook!"; return false;	
	//	m_FacebookSiteChanged = true;
	//	return false;
	//}

	//STEP 4. Making final POST request.

	//extracting post form id value
	FindLoc = RequestResult.find("post_form_id");
	FindLoc = RequestResult.find("value", FindLoc);
	char TempChars[2] = {34, 0};
	FindLoc = RequestResult.find(TempChars, FindLoc);
	if (FindLoc == std::string::npos)
	{
		m_FacebookSiteChanged = true;
		m_ErrorDescription = "FacebookAsync::AddApplicationToProfile(). STEP 4 ERROR. Error in extracting post form id value!";
		return false;
	}

	std::string PostFormID = RequestResult.substr(FindLoc+1, 32);

	//resetting the curl
	curl_easy_reset(m_pCurUser->m_pCurl);

	std::string POSTurl = FACEBOOK_ADDAPPURL;
	POSTurl.append("?next=");

	//setting request url
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_URL, POSTurl.c_str());

	//aggregating POST params
	std::string POSTparams = "";

	//allow_profile
	if (AllowProfile == true)
		POSTparams.append("allow_profile=on");
	//allow_nav_link
	if (AllowNavLink == true)
		POSTparams.append("&allow_nav_link=on");
	//allow_feeds
	if (AllowFeeds == true)
		POSTparams.append("&allow_feeds=on");
	//allow_profile_actions
	if (AllowProfileActions == true)
		POSTparams.append("&allow_profile_actions=on");
	
	//post form id
	POSTparams.append("&post_form_id=" + PostFormID);

	//api key
	POSTparams.append("&api_key=" + AppToAddKey);

	//install
	POSTparams.append("&install=1");

	//ref add
	POSTparams.append("&refadd=pd");

	//telling curl that it will be a POST request and setting POST fields
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_HTTPPOST, true );
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_POSTFIELDS, POSTparams.c_str());

	//enabling cookies
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_COOKIEFILE, "");

	//enabling redirections
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_MAXREDIRS, 100);

	//setting user agent - a subject to think about
	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_USERAGENT, FACEBOOK_USERAGENT);

	//setting the request result writing
	RequestResult = "";

	curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEFUNCTION, Facebook::Write_Callback);
    curl_easy_setopt(m_pCurUser->m_pCurl, CURLOPT_WRITEDATA, &RequestResult);	

	//making the request
	RequestResultCode = curl_easy_perform(m_pCurUser->m_pCurl);

	//checking if request was ok
	if (RequestResultCode != 0)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). Step 4 error. Can't make HTTP request: POST request for desktop app confirmation page!"; return false;	
	}

	if (RequestResult.size() < 1)
	{
		m_ErrorDescription = "Facebook::AddApplicationToProfile(). Step 4 error. Request result is empty(POST request for desktop app confirmation page)!"; return false;	
	}


	//now we must get a profile page

	//need to be checked out

	//THIS CHECK IS DISABLED
	//FindLoc = RequestResult.find(m_pCurUser->m_UserProfileInfo.Name);

	//if (FindLoc == std::string::npos)
	//{
	//	m_FacebookSiteChanged = true;
	//	m_ErrorDescription = "Facebook::AddApplicationToProfile(). STEP 4 ERROR. Wrong!";
	//	return false;
	//}
	//else
	//{
	//	m_ErrorDescription = "";
	//	return true;
	//}

	return true;

}//bool FacebookAsync::AddApplicationToProfile(const std::string& AppToAddKey, bool AllowProfile, bool AllowNavLink, bool AllowFeeds, bool AllowProfileActions)



bool FacebookAsync::CurUser_SendNotification(std::list<std::string>& UserIDs, const std::string& NotificationFBML)
{


	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_SendNotification(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_SendNotification(). No current user was set!"; return false;
	}

	if (m_pCurUser->m_CurLoginStage != LOGINSTAGE_SUCCEED)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_SendNotification(). Current user is not logged in!"; return false;
	}

	if (m_pCurUser->m_CurInfoStage != INFOSTAGE_SUCCEED)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_SendNotification(). Info wasn't fetched!"; return false;
	}



	//validating UserIDs list
	if (UserIDs.empty() == true)
	{
		m_ErrorDescription = "Facebook::CurUser_SendNotification(). User IDs list is empty!"; return false;
	}

	//validating notification FBML
	if (NotificationFBML.size() < 1)
	{
		m_ErrorDescription = "Facebook::CurUser_SendNotification(). Notification FBML string is empty!"; return false;
	}


	if (m_pCurUser->m_CurPhotoStage == PHOTOSTAGE_GETTINGUSERPHOTO 
		|| m_pCurUser->m_CurPhotoStage == PHOTOSTAGE_GETTINGFRIENDSPHOTOS
		|| m_pCurUser->m_CurPostFeedItemStage == POSTFEEDSTAGE_INPROGRESS
		|| m_pCurUser->m_CurNotificationStage == NOTIFSTAGE_INPROGRESS
		|| m_pCurUser->m_GenericCallInProgress == true)
	{
		//adding new notification to delayed vector
		m_pCurUser->m_DelayedNotifications.push_back(FacebookNotification(UserIDs, NotificationFBML));
		return true;
	}

	return m_pCurUser->SendNotification(UserIDs, NotificationFBML);

}


bool FacebookAsync::CurUser_PostFeedItem(FACEBOOK_FEEDITEM ItemType, const FacebookFeedItem& FeedItemInfo)
{

	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_PostFeedItem(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_PostFeedItem(). No current user was set!"; return false;
	}

	if (m_pCurUser->m_CurLoginStage != LOGINSTAGE_SUCCEED)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_PostFeedItem(). Current user is not logged in!"; return false;
	}

	if (m_pCurUser->m_CurInfoStage != INFOSTAGE_SUCCEED)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_PostFeedItem(). Info wasn't fetched!"; return false;
	}


	//validating FeedItemInfo
	if (FeedItemInfo.m_TitleFBML.size() < 1)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_PostFeedItem(). Invalid FeedItemInfo - title is missing!"; return false;
	}

	if (m_pCurUser->m_CurPhotoStage == PHOTOSTAGE_GETTINGUSERPHOTO 
		|| m_pCurUser->m_CurPhotoStage == PHOTOSTAGE_GETTINGFRIENDSPHOTOS
		|| m_pCurUser->m_CurPostFeedItemStage == POSTFEEDSTAGE_INPROGRESS
		|| m_pCurUser->m_CurNotificationStage == NOTIFSTAGE_INPROGRESS
		|| m_pCurUser->m_GenericCallInProgress == true)
	{
		//adding new feed item to delayed vector
		m_pCurUser->m_DelayedNewsFeedItems.push_back(FacebookNewsFeedItem(ItemType, FeedItemInfo));
		return true;
	}

	return m_pCurUser->PostFeedItem(ItemType, FeedItemInfo);

}


bool FacebookAsync::CurUser_MakeGenericCall(FacebookGenericCallData& CallData)
{


	//checking if init was called
	if (m_AppKey.size() < 1)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_MakeGenericCall(). Facebook::Init() wasn't called!"; return false;
	}

	//checking if cur user is ok
	if (m_pCurUser == NULL)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_MakeGenericCall(). No current user was set!"; return false;
	}

	if (m_pCurUser->m_CurLoginStage != LOGINSTAGE_SUCCEED)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_MakeGenericCall(). Current user is not logged in!"; return false;
	}

	if (m_pCurUser->m_CurInfoStage != INFOSTAGE_SUCCEED)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_MakeGenericCall(). Info wasn't fetched!"; return false;
	}


	//validating Generic Call
	if (CallData.m_Method.size() < 1)
	{
		m_ErrorDescription = "FacebookAsync::CurUser_MakeGenericCall(). Invalid method!"; return false;
	}

	if (m_pCurUser->m_CurPhotoStage == PHOTOSTAGE_GETTINGUSERPHOTO 
		|| m_pCurUser->m_CurPhotoStage == PHOTOSTAGE_GETTINGFRIENDSPHOTOS
		|| m_pCurUser->m_CurPostFeedItemStage == POSTFEEDSTAGE_INPROGRESS
		|| m_pCurUser->m_CurNotificationStage == NOTIFSTAGE_INPROGRESS
		|| m_pCurUser->m_GenericCallInProgress == true)
	{
		//adding new feed item to delayed vector
		m_pCurUser->m_DelayedGenericCalls.push_back(CallData);
		return true;
	}

	return m_pCurUser->MakeGenericCall(CallData);
}